Перейти к основному содержимому

7.06. Работа с Docker

Разработчику Архитектору Инженеру

Работа с Docker

Ресурсы

Прежде, чем работать с Docker, нужно позаботиться о ресурсах - их правильное распределение будет ключевым аспектом успешной работы.

Когда вы запускаете контейнеры, они используют ресурсы хостовой системы (допустим, виртуальной машины). Если контейнеры потребляют больше ресурсов, чем доступно на хосте, это приводит к проблемам:

  • нехватка памяти - ОС начинает использовать подкачку (swap), что сильно замедляет работу;
  • перегрузка CPU - процессор не справляется с нагрузкой, и система становится непригодной для использования;
  • завершение процессов - ОС может принудительно завершить процессы, чтобы освободить ресурсы.

Минимальные требования зависят от типа запускаемых контейнеров.

Нужно определить, сколько нужно самой системе и всем базовым программам на сервере или компьютере (допустим, 4-8 ГБ ОЗУ), и добавить к этому ресурсы для контейнеров.

Для простых и легковесных контейнеров - минимум 2 ГБ и 1 ядро.

То есть, если вы захотите развернуть докер на своём стареньком ПК с 2 ГБ и парой ядер - будьте уверены в том, что система не справится. А если, к примеру, вы разворачиваете веб-приложение, которое само по себе «кушает» 2-3 ГБ, а вдобавок ещё MongoDB, PostgreSQL/MySQL - каждый из них будет просить по 2 ГБ ОЗУ. И если добавить к ним ElasticSearch, то для него минимум 4-8 ОЗУ (для больших индексов нужно ещё больше).

Таком образом, если ссуммировать, получится так, что хост должен обладать не менее, чем 10 ГБ ОЗУ и 5 ядер CPU. К тому же, важно добавлять резерв - 20-30% для ОС и других процессов. К примеру, если контейнеры требуют 6,5 ГБ ОЗУ, хост должен иметь минимум 8-9 ГБ.

Поэтому просто развернуть кучу сервисов на слабом железе не получится. Если вы запускаете несколько контейнеров (веб-сервер, API, база данных), убедитесь, что суммарные ресурсы всех контейнеров не превышают доступные ресурсы хоста. Но Docker позволяет ограничить ресурсы для каждого контейнера. Это помогает предотвратить ситуацию, когда один контейнер «съедает» все ресурсы системы.

  1. Ограничение памяти.

Используйте флаг --memory для ограничения объёма ОЗУ, доступного контейнеру:

docker run -d --name my-container --memory="2g" my-image

В таком случае, контейнер сможет использовать не более 2 ГБ ОЗУ. Если контейнер попытается использовать больше памяти, он будет остановлен. 2. Ограничение CPU. Используйте флаги --cpus и --cpu-shares для управления CPU:

--cpus: ограничивает количество ядер, доступных контейнеру:

docker run -d --name my-container --cpus="1.5" my-image

здесь контейнер сможет использовать до 1,5 ядра.

--cpu-shares задаёт относительный вес для разделения CPU между контейнерами:

docker run -d --name my-container --cpu-shares=512 my-image

По умолчанию значение равно 1024. Чем меньше значение, тем меньше CPU получит контейнер.

  1. Ограничение подкачки (swap).

Используйте флаг --memory-swap, чтобы ограничить использование подкачки:

docker run -d --name my-container --memory="2g" --memory-swap="3g" my-image

Здесь общий лимит памяти (ОЗУ+swap) равен 3 ГБ. Если контейнер использует большк 3 ГБ, он будет остановлен.

  1. Использование Docker Compose для управления ресурсами.

В файле docker-compose.yml можно задать ограничения ресурсов. К примеру:

version: '3.8'
services:
mongodb:
image: mongo
deploy:
resources:
limits:
cpus: '1.5'
memory: 2g
elasticsearch:
image: elasticsearch:7.10.1
deploy:
resources:
limits:
cpus: '2'
memory: 4g

Как проверить использование ресурсов?

Чтобы избежать перегрузки системы, важно регулярно мониторить использование ресурсов. Команда docker stats показывает использование ресурсов всему запущенными контейнерами:

docker stats

В выводе будет указано использование CPU, ОЗУ, сети. Кроме docker-stats, важно использовать и стандартные инструменты мониторинга хостовой системы:

  • Linux : htop, free -h, vmstat.
  • Windows/macOS : Диспетчер задач или Activity Monitor.

Таким образом, чтобы грамотно распределить ресурсы, нужно правильно рассчитать их потребности, запастись памятью, ограничивать ресурсы, мониторить систему и не запускать слишком много контейнеров на одной машине.

Если ресурсов недостаточно, то выход - либо использовать их, либо использовать кластеризацию (Docker Swarm или Kubernetes).

Установка и настройка

Первым шагом в работе с контейнерами и системой контроля версий является установка и настройка таких инструментов, как Git и Docker. Эти процессы являются фундаментальными для современных практик разработки и требуют внимательного выполнения последовательности шагов. Прежде чем приступить к установке новых программных пакетов, необходимо обновить список доступных пакетов и их зависимостей. Это достигается с помощью команд sudo apt update и sudo apt upgrade. Мы их уже рассматривали ранее.

Команда apt update обновляет индекс пакетов из репозиториев, указанных в файле /etc/apt/sources.list, что позволяет получить актуальную информацию о доступных версиях программного обеспечения. После этого команда apt upgrade выполняет обновление установленных пакетов до последних версий. Этот процесс важен для получения исправлений безопасности и улучшений программного обеспечения.

Git — это распределенная система контроля версий, которая широко используется для управления исходным кодом проектов. Мы уже изучали Git ранее, но это важный момент. Установка Git выполняется через пакетный менеджер APT с помощью команды sudo apt install git. После завершения установки можно проверить её успешность, выполнив команду git --version, которая выводит текущую версию установленного Git. APT автоматически загружает все необходимые зависимости, что делает процесс установки простым и надежным.

Для работы с контейнерами необходимо установить Docker. Процесс установки Docker начинается с добавления официального репозитория Docker в систему.

Сначала выполняется обновление списка пакетов с помощью команды sudo apt update. Затем устанавливаются необходимые зависимости, такие как apt-transport-https, ca-certificates, curl и software-properties-common, с помощью команды sudo apt install apt-transport-https ca-certificates curl software-properties-common.

После этого добавляется GPG-ключ Docker с помощью команды:

curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -.

Далее настраивается репозиторий Docker с помощью команды:

sudo add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu focal stable"

Наконец, выполняется установка Docker Community Edition (CE) с помощью команды:

sudo apt install docker-ce

После установки Docker важно настроить его для удобства использования. По умолчанию выполнение команд Docker требует прав администратора (sudo). Однако это ограничение можно устранить, добавив пользователя в группу Docker. Для этого используется команда:

sudo usermod -aG docker ${USER}

После выполнения этой команды потребуется перезагрузка системы или выполнение команды newgrp docker для применения изменений. Чтобы проверить успешность добавления пользователя в группу Docker, можно использовать команду groups, которая выводит список групп текущего пользователя.

Когда Docker установится, нужно получить нужный исходный код для сборки (если, конечно, мы не хотим разворачивать уже существующий контейнер). Клонирование удалённого репозитория является одной из ключевых операций при работе с системой контроля версий Git, обеспечивающей создание точной локальной копии проекта для последующей разработки или анализа. Этот процесс выполняется с использованием команды git clone, которая копирует все файлы, ветки и историю коммитов исходного репозитория. Например, клонирование репозитория с платформы GitHub осуществляется следующим образом:

$ git clone https://github.com/user/repo.git

В результате выполнения этой команды создаётся новая директория с именем репозитория, содержащая полную копию данных. Это позволяет разработчикам быстро приступить к совместной работе над проектом, минуя этапы настройки структуры файлов и метаданных.

Сборка

Работа с Dockerfile является ключевым аспектом разработки и развертывания контейнеризованных приложений. Мы отдельно рассмотрим его, конечно, но сейчас пробежимся по общему шагу работы с ним.

Приведём пример Dockerfile для простого Node.js-приложения. Этот файл демонстрирует ключевые директивы, такие как FROM, RUN, COPY и CMD.

Вот пример:

# Базовый образ
FROM node:16-alpine

# Создание рабочей директории
WORKDIR /app

# Копирование package.json и установка зависимостей
COPY package*.json ./
RUN npm install

# Копирование исходного кода
COPY . .

# Команда для запуска приложения
CMD ["node", "index.js"]

Здесь директива FROM указывает базовый образ, который будет использоваться для создания нового образа, и используется официальный образ Node.js на основе Alpine Linux, что позволяет существенно уменьшить размер финального образа.

Директива WORKDIR задает рабочую директорию внутри контейнера, куда будут копироваться файлы и выполняться команды.

Инструкция COPY используется для копирования файлов из локальной файловой системы в контейнер. Например, COPY package*.json ./ копирует файлы package.json и package-lock.json в корневую директорию контейнера.

Команда RUN npm install устанавливает зависимости проекта.

CMD определяет команду, которая будет выполнена при запуске контейнера.

Для Python-приложений аналогичный Dockerfile может выглядеть следующим образом:

FROM python:3.10-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
CMD ["python", "app.py"]

Здесь используется базовый образ Python 3.10-slim, который также минимизирует размер образа за счет исключения ненужных компонентов. После создания Dockerfile можно приступить к сборке образа. Как мы уже рассмотрели ранее, для этого используется команда docker build:


docker build -t myapp:latest .

Здесь флаг -t задает тег для образа (в данном случае myapp:latest), а точка (.) указывает путь к контексту сборки, то есть директории, содержащей Dockerfile. После завершения сборки образ можно отправить в реестр с помощью команды docker push.

Пример многоэтапной сборки для Go-приложения:

# Стадия сборки
FROM golang:1.15 AS builder
WORKDIR /app
COPY . .
RUN go build -o myapp .

# Финальный образ
FROM gcr.io/distroless/static-debian10
COPY --from=builder /app/myapp /myapp
CMD ["/myapp"]

В этом примере первый этап использует образ golang:1.15 для компиляции исходного кода. На втором этапе создается минимальный образ на основе gcr.io/distroless/static-debian10, куда копируется только исполняемый файл.

Аналогичный метод может быть применен для Node.js-приложений. Например, на первой стадии можно установить все зависимости и выполнить сборку, а на второй — создать runtime-образ только с необходимыми файлами:

# Стадия сборки
FROM node:16-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
RUN npm run build

# Финальный образ
FROM node:16-alpine
WORKDIR /app
COPY --from=builder /app/dist ./dist
COPY package*.json ./
RUN npm install --production
CMD ["node", "dist/index.js"]

Здесь первая стадия выполняет установку зависимостей и сборку приложения, а вторая создает минималистичный образ, содержащий только скомпилированный код и runtime-зависимости.

При выполнении команды docker build Docker проходит несколько этапов:

  • Load Definition - загрузка Dockerfile и его разбор.
  • Metadata - сбор метаданных (например, имя образа, тег).
  • Transferring Context - передача контекста (файлов и папок) на Docker-демон.
  • Resolve - разрешение зависимостей и проверка наличия кэшированных слоёв.
  • Load Context - загрузка контекста в Docker.
  • Выполнение инструкций - пошаговое выполнение инструкций Dockerfile - apt-get update, COPY, RUN;
  • Exporting to Image - создание финального образа.
  • Layers - сохранение каждого слоя как отдельного объекта.
  • Manifest - создание манифеста образа (JSON с метаданными).
  • Config - настройка конфигурации (например, переменные окружения).
  • Attestation - добавление подписей или аттестаций (если настроено).
  • Naming - присвоение имени и тега образу.
  • Unpacking - подготовка образа для использования.

Запуск и управление контейнерами

Для запуска контейнера в фоновом режиме используется команда docker run с флагом -d, который указывает на работу контейнера в detached-режиме (фоновом).

Проброс портов осуществляется с помощью флага -p <хост_порт>:<контейнер_порт>. Например, команда docker run -d -p 8080:8080 <image_name> запускает контейнер из указанного образа <image_name>, привязывая порт 8080 хост-машины к порту 8080 внутри контейнера. Это позволяет внешним клиентам обращаться к сервису, работающему внутри контейнера, через указанный порт хоста. Важно учитывать, что при использовании брандмауэров, таких как ufw или firewalld, правила межсетевого экрана могут быть обойдены Docker. Для корректной настройки безопасности рекомендуется использовать iptables-nft или iptables-legacy и добавлять правила через цепочку DOCKER-USER.

Пример применения данной команды можно наблюдать в CI/CD-пайплайне, где после сборки образа Docker выполняется его запуск с пробросом портов для тестирования функциональности. После успешного тестирования контейнер может быть остановлен, а образ отправлен в реестр для дальнейшего использования в Kubernetes или других системах оркестрации.

Одним из важнейших аспектов управления контейнерами является возможность анализа их логов для диагностики проблем и мониторинга состояния. Для просмотра логов используется команда docker logs <container_id>, где <container_id> — это уникальный идентификатор запущенного контейнера. Эта команда выводит стандартные потоки вывода (stdout и stderr) контейнера, что позволяет отслеживать события внутри контейнера в реальном времени.

Например, если контейнер работает некорректно, можно выполнить команду docker logs <container_id> для получения информации о возможных ошибках. В случае необходимости можно добавить флаги, такие как --tail для ограничения количества последних строк логов или --follow для непрерывного отслеживания новых записей. Эти возможности особенно полезны при мониторинге производительности CI/CD-пайплайнов, где время сборки и частота развертываний являются ключевыми показателями эффективности.